package br.com.mizzu.system;

import java.io.File;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Set;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

import br.com.mizzu.annotations.Controller;
import br.com.mizzu.exceptions.ControllerNameException;


@WebListener
public class ContextListener implements ServletContextListener {
	private List<String> controllersNames = new ArrayList<String>();
	private static HashMap<String, String> controllersMap = new HashMap<String, String>();
	
	public static HashMap<String, String> getControllersMap() {
		return controllersMap;
	}

	@Override
	public void contextInitialized(ServletContextEvent event) {
		loadAnnotatedControllers();
	}

	@Override
	public void contextDestroyed(ServletContextEvent event) {
	}

	private void loadAnnotatedControllers(){
        scan(Thread.currentThread().getContextClassLoader(),  
                Collections.<String> emptySet(), Collections.<String> emptySet());  
        Class<?> clazz = null;
        for (int i = 0; i < controllersNames.size(); i++) {
        	try {
				clazz = Class.forName(controllersNames.get(i));
				Controller annotation = clazz.getAnnotation(Controller.class);
				String key;
				if(annotation!=null){
					String className = clazz.getName();
					if(annotation.nome().equals("")){
						key = (className.substring(className.lastIndexOf(".")+1)).replace("Controller", "");
					}else{
						key = annotation.nome();
					}
					if(controllersMap.containsKey(key)){
						throw new ControllerNameException("Controller com nome duplicado: "+ key + " em "+ clazz.getName());
					}else{
						controllersMap.put(key, className);
					}					
				}
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
		}
    }

	private void scan(ClassLoader classLoader, Set<String> locations,
			Set<String> packages) {
		if (!(classLoader instanceof URLClassLoader)) {
			return;
		}

		URLClassLoader urlLoader = (URLClassLoader) classLoader;
		URL[] urls = urlLoader.getURLs();

		for (URL url : urls) {
			String path = url.getFile();
			File location = null;
			try {
				location = new File(url.toURI());
			} catch (URISyntaxException e) {
				e.printStackTrace();
				return;
			}

			if (matchesAny(path, locations)) {
				if (location.isDirectory()) {
					getClassesInDirectory(null, location, packages);
				}
			}
		}
	}

	private void getClassesInDirectory(String parent, File location,
			Set<String> packagePatterns) {
		File[] files = location.listFiles();
		StringBuilder builder = null;

		for (File file : files) {
			builder = new StringBuilder(100);
			builder.append(parent).append("/").append(file.getName());
			String packageOrClass = (parent == null ? file.getName() : builder
					.toString());

			if (file.isDirectory()) {
				getClassesInDirectory(packageOrClass, file, packagePatterns);
			} else if (file.getName().endsWith("Controller.class")) {
				if (matchesAny(packageOrClass, packagePatterns)) {
					controllersNames.add((packageOrClass.replace(".class",
							"")).replace("/", "."));
				}
			}
		}
	}

	private boolean matchesAny(String text, Set<String> filters) {
		if (filters == null || filters.size() == 0) {
			return true;
		}
		for (String filter : filters) {
			if (text.indexOf(filter) != -1) {
				return true;
			}
		}
		return false;
	}
}
